/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import type * as vscode from 'vscode';
import { IChatEndpoint } from '../../../platform/networking/common/networking';
import { URI } from '../../../util/vs/base/common/uri';
import { IBuildPromptContext } from '../../prompt/common/intents';
import { ToolName } from './toolNames';

export interface IEditFilterData {
	title: string;
	message: string;
}

export enum CopilotToolMode {
	/**
	 * Give a shorter result, agent mode can call again to get more context
	*/
	PartialContext,

	/**
	 * Give a longer result, it gets one shot
	 */
	FullContext,
}

export interface ICopilotToolExtension<T> {
	/**
	 * Called when edits are made in a tool call response. The tool can return
	 * a confirmation that will be shown to the user before edits are applied.
	 */
	filterEdits?(resource: URI): Promise<IEditFilterData | undefined>;

	/**
	 * Called when the tool is referenced in a prompt. If this function is not
	 * provided, Copilot will ask the model to generate the necessary input.
	 */
	provideInput?(promptContext: IBuildPromptContext): Promise<T | undefined>;

	/**
	 * If set, called when the tool is selected in the tool calling loop. Can
	 * resolve LLM-generated input by adding properties or modifying generated
	 * ones.
	 * @param input Input that may have been generated by the model
	 * @param mode The mode in which the tool is being invoked
	 */
	resolveInput?(input: T, promptContext: IBuildPromptContext, mode: CopilotToolMode): Promise<T>;

	/**
	 * Optionally get a programmatic override for the LM tool information. This
	 * can be driven by EXP for example, or customized based on the current model.
	 * ⚠️ A tool using an alternative definition MUST still accept its default
	 * parameters because the alternative definition will only be applied within
	 * the Copilot extension, not other extensions' usages via `vscode.lm.tools`.
	 *
	 * @param tool The original tool definition.
	 * @param endpoint Optional information about the currently selected language model endpoint.
	 *                 If provided, allows customizing the tool definition per endpoint.
	 *
	 * @return An overridden tool definition.
	 */
	alternativeDefinition?(tool: vscode.LanguageModelToolInformation, endpoint?: IChatEndpoint): vscode.LanguageModelToolInformation;
}

export interface ICopilotTool<T> extends vscode.LanguageModelTool<T>, ICopilotToolExtension<T> {
}

export interface ICopilotToolCtor {
	readonly toolName: ToolName;
	new(...args: never[]): ICopilotTool<unknown>;
}

export interface ICopilotToolExtensionCtor {
	readonly toolName: ToolName;
	new(...args: never[]): ICopilotToolExtension<unknown>;
}

export const ToolRegistry = new class {
	private _tools: Array<ICopilotToolCtor> = [];
	private _toolExtensions: Array<ICopilotToolExtensionCtor> = [];

	public registerTool(tool: ICopilotToolCtor) {
		this._tools.push(tool);
	}

	public getTools(): readonly ICopilotToolCtor[] {
		return this._tools;
	}

	public registerToolExtension(tool: ICopilotToolExtensionCtor) {
		this._toolExtensions.push(tool);
	}

	public getToolExtensions(): readonly ICopilotToolExtensionCtor[] {
		return this._toolExtensions;
	}
}();
